﻿using System.Collections.Generic;
using System;
using System.Web;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using Com.FastSearch.Esp.Search.Result;
using Com.FastSearch.SharePoint.BL.SearchManager;
using Com.FastSearch.SharePoint.Interfaces;
using Com.FastSearch.SharePoint.WebControls.ESPSearchWebParts.UI;
using Com.FastSearch.SharePoint.WebControls.ESPSearchWebParts.UI.Client;
using ESPWebPartsUnitTest.mock;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ESPWebPartsUnitTest
{

    /// <summary>
    ///This is a test class for ESPSearchResultTest and is intended
    ///to contain all ESPSearchResultTest Unit Tests
    ///</summary>
    [TestClass]
    public class ESPSearchResultTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes

        private ESPSearchRequestMock searchSettings;

        [TestInitialize]
        public void TestInitialize()
        {
            searchSettings = new ESPSearchRequestMock();
            searchSettings.NavSplitCharacter = '\u01C1';
            searchSettings.Start1 = 0;
            searchSettings.NumberOfResultsPerPage = 10;
        }

        [TestCleanup]
        public void TestCleanup()
        {
            searchSettings = null;
        }
        
        #endregion

        /// <summary>
        ///A test for ExtractAppliedValues
        ///</summary>
        [TestMethod]
        public void ExtractAppliedValuesTest()
        {
            DoExtractAppliedValuesTest(new string[] { "companies\u01C1^Microsoft$\u01C1Microsoft\u01C1", "personnames\u01C1^\"Bill Gates\"$\u01C1Bill Gates\u01C1", "companies\u01C1^\"Fast Search & Transfer ASA\"$\u01C1Fast Search & Transfer ASA\u01C1" },
                                       new string[] { "companies", "personnames" },
                                       new string[][][] { new string[][] { new string[] { "companies", "^Microsoft$", "Microsoft" },
                                                                           new string[] { "companies", "^\"Fast Search & Transfer ASA\"$", "Fast Search & Transfer ASA" } },
                                                          new string[][] { new string[] { "personnames", "^\"Bill Gates\"$", "Bill Gates" } } });
            DoExtractAppliedValuesTest(new string[] { "xml\u01C1\"Bill Gates\"\u01C1Bill Gates\u01C1person@base", "xml\u01C1FAST\u01C1FAST\u01C1company@base" },
                                       new string[] { "xml" },
                                       new string[][][] { new string[][] { new string[] { "xml", "\"Bill Gates\"", "Bill Gates" },
                                                                           new string[] { "xml", "FAST", "FAST" } } });
        }

        /// <summary>
        ///A test for the basic MS Result xml object
        ///</summary>
        [TestMethod]
        public void BasicMSResultXMLTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(10, 100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertMSResultXML(xelem, 10);

        }

        private void AssertMSResultXML(XElement xelem, int numDocs)
        {
            var result_list = from result in xelem.Descendants("Result")
                              where result != null
                              select result;

            Assert.AreEqual<int>(numDocs, result_list.ToList().Count);
            foreach (var hit in result_list)
            {
                // Just check some samples
                Assert.AreEqual("Value0", hit.Element("Field0").Value);
                Assert.AreEqual("Value5", hit.Element("Field5").Value);
            }
        }

        /// <summary>
        ///A test for the basic MS Result xml object
        ///</summary>
        [TestMethod]
        public void NoResultsXMLTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(0, 0);
            searchSettings.Start1 = 0;
            searchSettings.NumberOfResultsPerPage = 0;
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertNoResultXML(xelem);

        }

        private void AssertNoResultXML(XElement xelem)
        {
            var noresults = from result in xelem.Descendants("NoResults")
                              where result != null
                              select result;

            Assert.AreEqual(1, noresults.ToList().Count, "No results message is not available!");

        }

        /// <summary>
        ///A test for HitHighliting xml nodes
        ///</summary>
        [TestMethod]
        public void HitHighlitingTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(10,100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultHithhighliting(xelem, 10);            
        }

        /// <summary>
        ///A test for HitHighliting xml nodes where the highliting has to fail for 2 nodes
        ///</summary>
        [TestMethod]
        public void HitHighlitingTestFailing()
        {
            QueryResultMock iqueryResult = new QueryResultMock(8, 100);
            iqueryResult.AddDocumentSummary(BuildDocumentsummaryForHighlightingFailed(11));
            iqueryResult.AddDocumentSummary(BuildDocumentsummaryForHighlightingFailed(12));
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultHithhighliting(xelem, 8);  // 2 shall silently fail as the highlighting is wrong in the source doc

        }

        /// <summary>
        ///A test for HitHighliting xml nodes
        ///</summary>
        [TestMethod]
        public void HitHighlitingpropertiesTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(10, 100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultHithhighlitingProperties(xelem, 10);

        }

        /// <summary>
        ///A test for HitHighliting xml nodes where the highliting has to fail for 2 nodes
        ///</summary>
        [TestMethod]
        public void HitHighlitingpropertiesTestFailing()
        {
            QueryResultMock iqueryResult = new QueryResultMock(8, 100);
            iqueryResult.AddDocumentSummary(BuildDocumentsummaryForHighlightingFailed(11));
            iqueryResult.AddDocumentSummary(BuildDocumentsummaryForHighlightingFailed(12));
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.XmlResult as XmlDocument;
            string s = doc.InnerXml;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultHithhighlitingProperties(xelem, 8);  // 2 shall silently fail as the highlighting is wrong in the source doc

        }

        private void AssertResultHithhighlitingProperties(XElement xelem, int numDocs)
        {
            var result_list = from result in xelem.Descendants("hithighlightedproperties")                           
                              where result != null
                              select result; 
                        

            Assert.AreEqual<int>(numDocs, result_list.ToList().Count);


            var result_list2 = from result in xelem.Descendants("hithighlightedproperties")
                               where (xelem.Element("hithighlightedproperties") != null)
                              select new
                              {
                                  URL = (String)result.Element("HHUrl").Value,
                                  TITLE = (String)result.Element("titlesummary").Value
                              };

            foreach (var hit in result_list2)
             {                 
                 Assert.IsTrue(!String.IsNullOrEmpty(hit.URL));
                 Assert.IsTrue(!String.IsNullOrEmpty(hit.TITLE));
             }            
        }



        /// <summary>
        ///A test for the did you mean xml object
        ///</summary>
        [TestMethod]
        public void ResultDidYouMeanXMLTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(10, 100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            ESPSearchRequestMock searchRequest = new ESPSearchRequestMock();
            searchRequest.QueryURLPropertyName = "k";

            HttpRequest request = new HttpRequest("test.aspx", "http://test.com/test.aspx", "k=Sugestion");

            XmlDocument doc = espsearch.GetXmlDidYouMean(request, ESPSearchGroupEnum.Group0, searchRequest) as XmlDocument;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertDidYouMeanXML(xelem, "/test.aspx?k=Suggestion");
        }

        /// <summary>
        ///A test for the did you mean xml object if the webpart is in a search group
        ///</summary>
        [TestMethod]
        public void ResultDidYouMeanXMLTestWithGroup()
        {
            IQueryResult iqueryResult = new QueryResultMock(10, 100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            ESPSearchRequestMock searchRequest = new ESPSearchRequestMock();
            searchRequest.QueryURLPropertyName = "q";

            HttpRequest request = new HttpRequest("test.aspx", "http://test.com/test.aspx", 
                searchRequest.QueryURLPropertyName + 
                SearchRequest.GetPropertyPostfixForGroup(ESPSearchGroupEnum.Group1) + "=Sugestion");
            
            XmlDocument doc = espsearch.GetXmlDidYouMean(request, ESPSearchGroupEnum.Group1,searchRequest) as XmlDocument;
            XElement xelem = XElement.Parse(doc.InnerXml);
            string expectedurl = "/test.aspx?" + searchRequest.QueryURLPropertyName +
                                 SearchRequest.GetPropertyPostfixForGroup(ESPSearchGroupEnum.Group1) +
                                 "=Suggestion";

            AssertDidYouMeanXML(xelem, expectedurl);                                
        }


        private void AssertDidYouMeanXML(XElement xelem, string expectedSuggestionUrl)
        {
            var suggestions = from result in xelem.Descendants("Suggestion")
                              where result != null
                              select new
                              {
                                  URI = (String)result.Element("SuggestedQueryUri").Value,
                                  SUGGESTED = (String)result.Element("SuggestedQuery").Value,
                                  ORIGINAL = (String)result.Element("OriginalQuery").Value
                              };


            Assert.AreEqual<int>(1, suggestions.ToList().Count);

            foreach (var suggestion in suggestions)
            {
                Assert.AreEqual("Suggestion", suggestion.SUGGESTED);
                Assert.AreEqual("Sugestion", suggestion.ORIGINAL);
                Assert.AreEqual(expectedSuggestionUrl, suggestion.URI);                
            }


            var modifications = from result in xelem.Descendants("Modification")
                              where result != null
                              select new
                              {                                
                                  MODIFIED = (String)result.Element("ModifiedQuery").Value,
                                  ORIGINAL = (String)result.Element("OriginalQuery").Value
                              };

            Assert.AreEqual<int>(1, modifications.ToList().Count);
            foreach (var modification in modifications)
            {
                Assert.AreEqual("Modification", modification.MODIFIED);
                Assert.AreEqual("Moification", modification.ORIGINAL);                
            }     


        }

        

        /// <summary>
        ///A test for the basic MS Result xml object
        ///</summary>
        [TestMethod]
        public void ResultStatisticsXMLTest()
        {
            IQueryResult iqueryResult = new QueryResultMock(10, 100);
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.GetXmlSearchStatistics(0, 10) as XmlDocument;            
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultStatisticsXML(xelem, 1,10,100, "0.5");
        }

        /// <summary>
        ///A test for the basic MS Result xml object
        ///</summary>
        [TestMethod]
        public void ResultStatisticsXMLTest2()
        {
            IQueryResult iqueryResult = new QueryResultMock(5, 100, 95);
            searchSettings.Start1 = 95;
            searchSettings.NumberOfResultsPerPage = 5;
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.GetXmlSearchStatistics(95, 5) as XmlDocument;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultStatisticsXML(xelem, 96, 100, 100, "0.5");
        }

        /// <summary>
        ///A test for the basic MS Result xml object
        ///</summary>
        [TestMethod]
        public void ResultStatisticsXMLTest3()
        {
            IQueryResult iqueryResult = new QueryResultMock(5, 100, 95);
            searchSettings.Start1 = 96;
            searchSettings.NumberOfResultsPerPage = 5;
            ESPSearchResult espsearch = new ESPSearchResult(iqueryResult, searchSettings);
            XmlDocument doc = espsearch.GetXmlSearchStatistics(96, 5) as XmlDocument;
            XElement xelem = XElement.Parse(doc.InnerXml);
            AssertResultStatisticsXML(xelem, 97, 100, 100, "0.5");
        }

        private void AssertResultStatisticsXML(XElement xelem, int from, int to, int total, string time)
        {
            var result = from stat in xelem.Elements()           
                         where stat != null
                         select stat;
                        
            List<XElement> l = result.ToList();
            Assert.AreEqual(4, l.Count, "attributes not found!");
            Assert.AreEqual(from.ToString(), l[0].Value);
            Assert.AreEqual(to.ToString(), l[1].Value);
            Assert.AreEqual(total.ToString(), l[2].Value);
            Assert.AreEqual(time, l[3].Value);      
        }

        /// <summary>
        /// Build a docuyment summary which contains a wrong highlighting. NOTE: &gt;/b&lt; as opening tag
        /// </summary>
        /// <param name="docNum"> document number in the document set</param>
        /// <returns></returns>
        private DocumentSummaryMock BuildDocumentsummaryForHighlightingFailed(int docNum)
        {          
            return BuildDocumentsummary("<b>Title</b> with some </b>highlighting</b>",
                "<b>This</b> is the body with some </b>highlighting</b>", 
                "http://test.com/" + docNum , docNum);
        }

        private DocumentSummaryMock BuildDocumentsummary(string title, string body, string url, int docNum)
        {
            DocumentSummaryMock docsumMock = new DocumentSummaryMock(10, docNum);
            docsumMock.AddSummary("title", title, title);
            docsumMock.AddSummary("body", body, body);
            docsumMock.AddSummary("url", url, url);
            return docsumMock;

        }

        private void AssertResultHithhighliting(XElement xelem, int numDocs)
        {
            var result_list = from result in xelem.Descendants("hithighlightedsummary")                          
                         where result != null select result;                         

            Assert.AreEqual<int>(numDocs, result_list.ToList().Count);            
        }

        private void DoExtractAppliedValuesTest(string[] drillDown, string[] navigators, string[][][] modifiers)
        {
            Assert.AreEqual(navigators.Length, modifiers.Length);
            IDictionary<string, IList<IESPNavigatorItem>> expected = new Dictionary<string, IList<IESPNavigatorItem>>();
            for (int i = 0; i < navigators.Length; ++i)
            {
                IList<IESPNavigatorItem> mods = new List<IESPNavigatorItem>();
                foreach (string[] modifier in modifiers[i])
                {
                    ESPNavigatorItemMock mod = new ESPNavigatorItemMock();
                    mod.FieldName = modifier[0];
                    mod.FilterValue = modifier[1];
                    mod.DisplayValue = modifier[2];
                    mods.Add(mod);
                }
                expected[navigators[i]] = mods;
            }
            IDictionary<string, IList<IESPNavigatorItem>> actual = new Dictionary<string, IList<IESPNavigatorItem>>();
            ESPSearchResult.ExtractAppliedValues(actual, drillDown, new char[] { searchSettings.NavSplitCharacter });
            AssertEqual(expected, actual);
        }

        private static void AssertEqual(IDictionary<string, IList<IESPNavigatorItem>> d1, IDictionary<string, IList<IESPNavigatorItem>> d2)
        {
            Assert.AreEqual(d1.Count, d2.Count);
            foreach (string key in d1.Keys)
            {
                Assert.IsTrue(d2.ContainsKey(key));
                Assert.AreEqual(d1[key].Count, d2[key].Count);
                foreach (IESPNavigatorItem val in d1[key])
                {
                    Assert.IsTrue(d2[key].Contains(val));
                }
            }
        }


    }
}
